from lexerV3 import yacc
from lexerV3 import lex
import re

diccLexemas = dict([])
globTokens =''

#Siendo sourceCode un str con codigo fuente de Metalangue, devuelve la 
#lista de tokens (como son construidos por PLY) que resultan del analisis lexico.
#Si el codigo no es valido, se debe levantar una excepcion con el error correspondiente. 

def metalangue_tokens(sourceCode):
	try:
		listaTok=[]
		lex.lexer.input(sourceCode)
		for tok in lex.lexer:
			listaTok.append(tok)
		return listaTok	
	except Exception as exce:
		print exce
	

#Retorna el arbol de sintaxis abstracta que resulta del parsing del codigo en el str sourceCode. 
#Si el codigo no es valido se debe levantar una excepcion con el error correspondiente.

def metalangue_ast(sourceCode):
	try:
		salida = yacc.parse(sourceCode)
		return salida
	except Exception as exce:
		print exce 
	


#Retorna el codigo objeto generado a partir del codigo fuente dado por sourceCode.
#Ambos sourceCode y el resultado son de tipo str.
#Si el codigo no es valido se debe levantar una excepcion con el error correspondiente.

def metalangue_compilation(sourceCode):
	try:
		global globTokens
		globTokens = genlex(sourceCode,diccLexemas)
		salida = globTokens
		salida = salida + genReserv(diccLexemas)
		salida = salida + genTokens(diccLexemas)
		salida = salida + genIgnore(sourceCode) #Generar las sentencias del codigo a ignorar
		salida = salida + genprec(sourceCode,diccLexemas) #Generar el codigo de las precedencias
		salida = salida + genClass(sourceCode) #Generar el codigo de las clases'''
		salida = salida + genRules(sourceCode,diccLexemas) #Generar el codigo de las Reglas
		return salida	
	except Exception as exce:
		print exce
		

#Levanta el codigo fuente de metalangue en el archivo sourceFile y escribe el codigo objeto generado en el archivo objectFile.
#Ambos argumentos pueden ser rutas tanto absolutas como relativas.
#Si el codigo no es valido se debe levantar una excepcion con el error correspondiente.       
def metalangue_compileFile(sourceFile, objectFile):
	try:
		entrada = readFile(sourceFile)
		if(checkType(entrada)):
			if(checkNT(entrada)):
				salida = metalangue_compilation(entrada)
				writeFile(salida,objectFile)
			else:
				print "Error: No terminal sin definir"	
		else:
			print""
	except Exception as excep:
		print excep			
				


def genprec(entrada,lexemas):
	precedence = yacc.parse(entrada).genprec(lexemas)
	if (len(precedence)>0):
		precedence = 'precedence = (' + precedence
		precedence = precedence[0:len(precedence)-1] + ')\n\n'		

	return precedence	


def genTokens(diccLexemas ):
	salida='tokens = ['
	for elemento in diccLexemas:
		try:
			m = re.match(r'[a-zA-Z]+',elemento)
			if m==None:
				salida+="'"+diccLexemas[elemento]+"',"
		except:
			pass
	salida = salida[0:len(salida)-1]  
	salida += "]+list(reserved.values())\n\n"
	salida += """def t_error(t):\n\t print("Illegal character '%s'" % t.value[0]) \n\t t.lexer.skip(1)\n\n"""
	return salida

def genReserv(lexemas):
	salida = "\nreserved = {\n"
	for palabra in lexemas:
		try:
			m = re.match(r'[a-zA-Z]+',palabra)
			if m!=None:
				salida += "\t'"+palabra+"':"+"'"+palabra.upper()+"',\n"
		except:
			pass 
	salida=salida[0:len(salida)-2]		
	salida += "\n}\n\n"
	return salida	

def genlex(entrada,diccLexemas ):
	lista = yacc.parse(entrada).genLex()
	cant = 0
	salida = ''
	for elemento in lista:
		primerSimbolo= elemento[0]
		if primerSimbolo == "'":
			segundaComilla = elemento.find("'",2)
			simbolo = elemento[1:segundaComilla]
			if ( not diccLexemas.has_key(simbolo)):
				try:
					m = re.match(r'[a-zA-Z]+',simbolo)#aca se generan los tokens que son palabras reservadas
					if m!=None:
						diccLexemas[simbolo]=simbolo.upper()
					else:
						simboloNuevo=''
						for caracter in simbolo:	
							if (caracter.find(".")==-1 and caracter.find("^")==-1 and caracter.find("$")==-1 and caracter.find("*")==-1 and caracter.find("+")==-1 and caracter.find("?")==-1 and caracter.find("{")==-1 and caracter.find("}")==-1 and caracter.find("[")==-1 and caracter.find("]")==-1 and caracter.find("'\'")==-1 and caracter.find("|")==-1 and caracter.find("(")==-1 and caracter.find(")")==-1):
								simboloNuevo = simboloNuevo + caracter
							else:
								simboloNuevo = simboloNuevo + '\\' + caracter	 
						nombreToken = 't_TOKEN'+str(cant)
						token = "r"+"'"+simboloNuevo+"'"
						diccLexemas[simbolo]='TOKEN'+str(cant)
						salida = salida + nombreToken+"= " +token+'\n' 
						cant += 1 
				except:
					pass
		else:#Aca se genera los tokens de las ER
			if primerSimbolo == '/':
				elementoNuevo=''
				for caracter in elemento:
					if(caracter =="'"):
						elementoNuevo = elementoNuevo + '\\'+caracter
					else:
						elementoNuevo = elementoNuevo + caracter	
				segundaBarra = len(elemento)-1
				segundaBarraNueva = len(elementoNuevo)-1
				simbolo = elemento[1:segundaBarra]
				simboloNuevo = elementoNuevo[1:segundaBarraNueva]
				nombreToken = 't_TOKEN'+str(cant)
				token = "r"+"'"+simboloNuevo+"'"
				diccLexemas[simbolo]='TOKEN'+str(cant)
				salida = salida + nombreToken+"= "+token +'\n'
				cant += 1 
	return salida


def genClass(entrada):
	dic={}
	yacc.parse(entrada).genClass(dic)
	salida=''
	salida = 'from collections import namedtuple \n\n' + salida
	for clase in dic :
		if dic[clase]==[]:
			salida=salida + 'class '+ clase +'():\n\tpass\n\n'
	for clase in dic :
		if not dic[clase]==[]:		
			salida=salida + 'class '+ clase +'('
			aux=dic[clase]
			if(len(aux)>1):
				salida= salida + 'namedtuple('+"'"+clase+"'"
				salida = salida + ", '"
				for elemento in aux[1]:
					salida = salida +elemento+' '	
				salida=salida[0:len(salida)-1]		
				salida= salida +"'), "+(aux[0])[0]+'):\n\tpass\n\n'
			else:
				salida=salida+(aux[0])[0]+'):\n\tpass\n\n'		
	return salida



def checkType(entrada):
	salida = yacc.parse(entrada).checkType()
	return salida

def checkNT(entrada):
	lCat = yacc.parse(entrada).getCat()
	salida = yacc.parse(entrada).checkNT(lCat)
	return salida


def genIgnore(entrada):
	salida = yacc.parse(entrada).genIgnore()
	return salida+'\n'


def genRules(entrada,lexemas):
	print lexemas
	salida=''
	posInicial = entrada.find('%start')
	if posInicial!=-1:
		finInicial = entrada.find('>',posInicial)
		simboloInicial = entrada[posInicial+6:finInicial-1]
		salida = "start = '"+simboloInicial.strip()+"'\n"
	salida += yacc.parse(entrada).genRules(lexemas)
	salida = salida + """\n\ndef p_error(t):\n\tif hasattr(t, 'value'):\n\t\tprint("Syntax error at '%s'" % t.value)\n\telse:\n\t\tprint ("Syntex error")\n\t"""
	return salida


	
def readFile(source_path):
	f = open(source_path, "r")
	linea = f.read()
	return linea


def writeFile(salida, target_path):
	f=open(target_path,"w")
	f.write(salida)
	f.close()


if __name__ == '__main__':
	import sys
	print 'Ingrese el nombre de archivo .langue' 
	salida=raw_input(" ")
	entrada = salida
	print 'Ingrese el nombre de archivo .py'
	salida=raw_input()
	metalangue_compileFile(entrada+".langue", salida+".py")
#Para pruebas y depuracion:
#unparse();
#getAST();		
#genprec();
#genClass();

